package workers

import (
	"bytes"
	"fmt"
	"github.com/go-cinch/common/log"
	"go-tiktok-scrapy/pkg/models"
	rds "go-tiktok-scrapy/pkg/redis"
	"go-tiktok-scrapy/pkg/regex"
	"strings"
	"time"

	"github.com/PuerkitoBio/goquery"
	"github.com/gocolly/colly/v2"
)

var tikTokWorker *TikTokWorker

const tiktokJobQueue = "tiktokJobQueue"

func RunTikTokWorker(count int) {
	log.Info("worker is running %d", count)
	tikTokWorker = NewTikTokWorker()
	go func() {
		for {
			if len(tikTokWorker.jobsArr) > 100 {
				time.Sleep(time.Millisecond * 200)
				continue
			}
			val := rds.Redis.LPop(tiktokJobQueue)
			if val != "" {
				if tikTokWorker.isJobScraped(val) {
					continue
				}
				if err := tikTokWorker.AddJob(val); err != nil {
					log.Error(err.Error())
					continue
				}
			}
			time.Sleep(time.Millisecond * 200)
		}
	}()

	tikTokWorker.run(count)
}

func NewTikTokWorker() *TikTokWorker {
	return &TikTokWorker{}
}

func (w *TikTokWorker) parse(r *colly.Response, handler string, proxy string) *models.TikTok {
	tiktok := &models.TikTok{}

	tiktok.Handler = handler

	doc, _ := goquery.NewDocumentFromReader(bytes.NewReader(r.Body))
	title := strings.TrimSpace(doc.Find(".title-m").Text())
	if title != "" || r.StatusCode != 200 {
		log.Error("error found | msg %s | job %s | status code %d | proxy %s | body %s ", title, handler, r.StatusCode, proxy, string(r.Body))
		return nil
	}
	title = strings.TrimSpace(doc.Find("[data-e2e='user-title']").Text())
	if title == "" {
		log.Error("error found | title is empty %s | job %s | status code %d | proxy %s | body %s ", title, handler, r.StatusCode, proxy, string(r.Body))
		return nil
	}

	doc.Find("[data-e2e='user-subtitle']").Each(func(i int, s *goquery.Selection) {
		tiktok.Username = s.Text()
	})

	doc.Find("[data-e2e='following-count']").Each(func(i int, s *goquery.Selection) {
		followingCount, err := regex.ParseStrToInt(s.Text())
		if err != nil {
			log.Error("failed to parse following count to int | %s | err: %s", s.Text(), err.Error())
			return
		}
		tiktok.FollowingCount = followingCount
	})

	doc.Find("[data-e2e='followers-count']").Each(func(i int, s *goquery.Selection) {
		followersCount, err := regex.ParseStrToInt(s.Text())
		if err != nil {
			log.Error("failed to parse followers count to int | %s | err: %s", s.Text(), err.Error())
			return
		}
		tiktok.FollowersCount = followersCount
	})

	doc.Find("[data-e2e='likes-count']").Each(func(i int, s *goquery.Selection) {
		likesCount, err := regex.ParseStrToInt(s.Text())
		if err != nil {
			log.Error("failed to parse likes count to int | %s | err: %s", s.Text(), err.Error())
			return
		}
		tiktok.LikesCount = likesCount
	})

	doc.Find("[data-e2e='user-bio']").Each(func(i int, s *goquery.Selection) {
		tiktok.Bio = s.Text()
	})

	doc.Find("[data-e2e='user-link'] span").Each(func(i int, s *goquery.Selection) {
		tiktok.Links = append(tiktok.Links, s.Text())
	})
	doc.Find("[class*='ImgAvatar']").Each(func(i int, s *goquery.Selection) {
		profile, ok := s.Attr("src")
		if ok {
			tiktok.ProfilePic = profile
		}
	})

	doc.Find("svg [fill-rule='evenodd']").Each(func(i int, s *goquery.Selection) {
		_, ok := s.Attr("fill")
		if ok {
			tiktok.BlueTick = true
		}
	})

	tiktok.Emails = regex.ParseEmails(tiktok.Bio)
	if tiktok.LikesCount == 0 && tiktok.FollowersCount == 0 {
		rds.Redis.LPush(tiktokJobQueue, handler)
		log.Error(`failed to scrape data from handler %s | %+v | resp body %s`, handler, tiktok, string(r.Body))

		return nil
	}
	return tiktok

}

func (w *TikTokWorker) worker(job string) {

	w.markJobScraped(job)

	c := newCollector()
	c.SetRequestTimeout(30 * time.Second)

	//proxy := getProxy()
	//if proxy != "" {
	//	c.SetProxy(proxy)
	//}

	proxy := ""

	c.OnError(func(r *colly.Response, err error) {
		log.Error("error found | msg %s | job %s | url %s | status code %d | proxy %s | resp %s", err.Error(), job, r.Request.URL, r.StatusCode, proxy, string(r.Body))
		w.incrError(job)
		if r.StatusCode == 404 {
			return
		}
		w.AddJob(job)

	})
	c.OnResponse(func(r *colly.Response) {
		tiktok := w.parse(r, job, "")
		if tiktok == nil {
			w.incrError(job)
			w.AddJob(job)
			return
		}
		log.Info("TikTok %+v", tiktok)
		if err := tiktok.Insert(); err != nil {
			log.Error(err.Error())
		} else {
			w.incrSuccess()
			log.Info("Tiktok handler %s is saved into db successfully !", job)
		}

	})
	c.OnRequest(func(r *colly.Request) {
		r.Headers.Set("referer", "https://www.google.com")
		r.Headers.Set("accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7")
		r.Headers.Set("accept-language", "en-CA,en;q=0.9")
		r.Headers.Set("cache-control", "no-cache")
		r.Headers.Set("dnt", "1")
		r.Headers.Set("pragma", "no-cache")
		r.Headers.Set("sec-fetch-dest", "document")
		r.Headers.Set("sec-fetch-mode", "navigate")
		r.Headers.Set("sec-fetch-site", "none")
		r.Headers.Set("sec-fetch-user", "?1")
		r.Headers.Set("upgrade-insecure-requests", "1")
		log.Info("Visiting %s", r.URL.String())
	})
	c.Visit(fmt.Sprintf("https://www.tiktok.com/@%s?lang=en", job))
}
